﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Orchard.Data.Providers;
using FluentNHibernate.Cfg.Db;
using NHibernate.Cfg;
using FluentNHibernate.Cfg;
using FluentNHibernate.Automapping;
using Orchard.Environment.ShellBuilders.Models;
using FluentNHibernate.Conventions.Helpers;
using Orchard.Data.Conventions;
using FluentNHibernate.Automapping.Alterations;
using Orchard.Data;
using Orchard.ContentManagement.Records;
using NHibernate.Event.Default;
using NHibernate.Event;
using NHibernate.Engine;
using NHibernate.Persister.Entity;
using NHibernate;

namespace Orchard.Contrib.Modules.NHibSecondLevelCache.Data.Providers
{
    [Serializable]
    public abstract class AbstractDataServicesProvider : Orchard.Data.Providers.AbstractDataServicesProvider, IDataServicesProvider
    {
        public new Configuration BuildConfiguration(SessionFactoryParameters parameters)
        {
            var database = GetPersistenceConfigurer(parameters.CreateDatabase);
            var persistenceModel = CreatePersistenceModel(parameters.RecordDescriptors);

            return Fluently.Configure()
                .Database(database)
                .Mappings(m => m.AutoMappings.Add(persistenceModel))
                .ExposeConfiguration(cfg => cfg.EventListeners.LoadEventListeners = new ILoadEventListener[] { new OrchardLoadEventListener() })
                .BuildConfiguration();
        }

        public new static AutoPersistenceModel CreatePersistenceModel(IEnumerable<RecordBlueprint> recordDescriptors)
        {
            var pModel = Orchard.Data.Providers.AbstractDataServicesProvider.CreatePersistenceModel(recordDescriptors);
            return pModel.Conventions.Add(new CacheConvention(recordDescriptors));
        }

        [Serializable]
        class OrchardLoadEventListener : DefaultLoadEventListener, ILoadEventListener
        {

            public new void OnLoad(LoadEvent @event, LoadType loadType)
            {
                ISessionImplementor source = (ISessionImplementor)@event.Session;
                IEntityPersister entityPersister;
                if (@event.InstanceToLoad != null)
                {
                    entityPersister = source.GetEntityPersister((string)null, @event.InstanceToLoad);
                    @event.EntityClassName = @event.InstanceToLoad.GetType().FullName;
                }
                else
                    entityPersister = source.Factory.GetEntityPersister(@event.EntityClassName);
                if (entityPersister == null)
                    throw new HibernateException("Unable to locate persister: " + @event.EntityClassName);

                //a hack to handle unused ContentPartRecord proxies on ContentItemRecord or ContentItemVersionRecord.
                //I don't know why it actually works, or how to do it right

                //if (!entityPersister.IdentifierType.IsComponentType)
                //{
                //    Type returnedClass = entityPersister.IdentifierType.ReturnedClass;
                //    if (returnedClass != null && !returnedClass.IsInstanceOfType(@event.EntityId))
                //        throw new TypeMismatchException(string.Concat(new object[4]
                //    {
                //      (object) "Provided id of the wrong type. Expected: ",
                //      (object) returnedClass,
                //      (object) ", got ",
                //      (object) @event.EntityId.GetType()
                //    }));
                //}

                EntityKey keyToLoad = new EntityKey(@event.EntityId, entityPersister, source.EntityMode);

                if (loadType.IsNakedEntityReturned)
                    @event.Result = this.Load(@event, entityPersister, keyToLoad, loadType);
                else if (@event.LockMode == LockMode.None)
                    @event.Result = this.ProxyOrLoad(@event, entityPersister, keyToLoad, loadType);
                else
                    @event.Result = this.LockAndLoad(@event, entityPersister, keyToLoad, loadType, source);
            }
        }
    }
 }